﻿using HamcoWcs.Models.Common;
using System.Linq.Expressions;
using System.Reflection;

namespace System.Linq
{
    public static class LinqTool
    {

        #region Export Methods
        /// <summary>
        /// Vuage query
        /// </summary>
        /// <remarks>
        /// Some query properties need use <see cref="VuageQueryAttribute"/> to let vuagequery valid.
        /// </remarks>
        /// <typeparam name="T">Queryable type</typeparam>
        /// <typeparam name="TQuery">Query Dto type</typeparam>
        /// <param name="source">IQueryable</param>
        /// <param name="query">QueryDto</param>
        /// <param name="count">Total data count</param>
        /// <param name="pageCount">Pagion page count</param>
        /// <returns>IQueryable</returns>
        public static IQueryable<T> VagueWhereOut<T, TQuery>(this IQueryable<T> source, TQuery query, out int count, out int pageCount)
        {
            // 模糊查询
            source = source.DoVuageQuery(query, out int page, out int size);
            // 分页
            source = source.Pagion(page, size, out int tcount, out int tpageCount);
            count = tcount;
            pageCount = tpageCount;
            return source;
        }
        public static IQueryable<T> WhereIf<T>(this IQueryable<T> source, Expression<Func<T, bool>> predicate, bool condition)
        {

            return condition ? source.Where(predicate) : source;
        }

        public static IQueryable<T> WhereIf<T>(this IQueryable<T> source, Expression<Func<T, bool>> predicate, string searchStr)
        {
            return searchStr.IsValid() ? source.Where(predicate) : source;
        }

        public static IQueryable<T> Pagion<T>(this IQueryable<T> source, int page, int size, out int count, out int pageCount)
        {
            try
            {
                if (page <= 0) page = 1;
                if (size <= 0) size = 10;
                count = source?.Count() ?? 0;
                pageCount = ((count - 1) / size) + 1;
                return source.Skip((page - 1) * size)
                             .Take(size);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
            count = 0;
            pageCount = 0;
            return source.Skip((page - 1) * size).Take(size); ;
        }
        #endregion

        private static IQueryable<T> DoVuageQuery<T, TQuery>(this IQueryable<T> source, TQuery query, out int page, out int size)
        {
            page = 1;
            size = 10;
            var qProps = typeof(TQuery).GetProperties();
            foreach (var qProp in qProps)
            {
                var value = qProp.GetValue(query);
                if (value == null) continue;


                var attr = qProp.GetCustomAttribute<VuageQueryAttribute>();
                object defaultValue = null;
                string targetPropName = "";
                EnumVuageQueryType queryType = EnumVuageQueryType.StringContains;

                if (attr != null)
                {

                    if (!string.IsNullOrEmpty(attr.TargetPropertyName))
                    {
                        targetPropName = attr.TargetPropertyName;
                    }
                    else
                    {
                        targetPropName = qProp.Name;
                    }
                    defaultValue = attr.DefauleValue;
                    queryType = attr.QueryType;



                    switch (queryType)
                    {
                        case EnumVuageQueryType.StringContains:
                            var express = WhereStringContains<T>(qProp.Name, value);
                            source = source.Where(express);
                            break;
                        case EnumVuageQueryType.StartTime:
                            if ((DateTime)value != default(DateTime))
                            {
                                express = WhereGreaterEqual<T>(targetPropName, value);
                                source = source.Where(express);
                            }

                            break;
                        case EnumVuageQueryType.EndTime:
                            if ((DateTime)value != default(DateTime))
                            {
                                express = WhereLessEqual<T>(targetPropName, value);
                                source = source.Where(express);
                            }
                            break;
                        case EnumVuageQueryType.String:
                        case EnumVuageQueryType.Long:
                        case EnumVuageQueryType.Int:
                        case EnumVuageQueryType.Double:
                        case EnumVuageQueryType.Float:
                            if (!value.Equals(defaultValue))
                            {
                                express = WhereEqual<T>(qProp.Name, value);
                                source = source.Where(express);
                            }
                            break;
                        case EnumVuageQueryType.Size:
                            try
                            {
                                var tsize = (int)value;
                                size = tsize > 0 ? tsize : 1;
                            }
                            catch (Exception)
                            {

                            }
                            break;
                        case EnumVuageQueryType.Page:
                            try
                            {
                                var tpage = (int)value;
                                page = tpage > 0 ? tpage : 1;
                            }
                            catch (Exception)
                            {

                            }
                            break;
                        case EnumVuageQueryType.Ignore:
                        default:
                            break;
                    }
                }
                else
                {
                    var pType = qProp.PropertyType;
                    if (pType == typeof(string))
                    {
                        var express = WhereStringContains<T>(qProp.Name, value);
                        source = source.Where(express);
                    }
                    else if (pType == typeof(int?)
                          || pType == typeof(long?)
                          || pType == typeof(float?)
                          || pType == typeof(short?)
                          || pType == typeof(double?))
                    {
                        var express = WhereEqual<T>(qProp.Name, value);
                        source = source.Where(express);
                    }
                    else if (pType == typeof(bool) || pType == typeof(bool?))
                    {
                        var express = WhereEqual<T>(qProp.Name, value);
                        source = source.Where(express);
                    }
                }
            }
            return source;
        }

        #region static expression methods
        public static Expression<Func<T, bool>> WhereStringContains<T>(string propName, object value)
        {
            var str = value.ToString();
            var param = Expression.Parameter(typeof(T), "x");
            var paramPi = Expression.Property(param, propName);
            var queryValue = Expression.Constant(value);
            var containExp = Expression.Call(paramPi, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), queryValue);
            var lambda = Expression.Lambda<Func<T, bool>>(containExp, param);
            return lambda;
        }
        public static Expression<Func<T, bool>> WhereEqual<T>(string propName, object value)
        {
            var param = Expression.Parameter(typeof(T), "x");
            var paramPi = Expression.Property(param, propName);
            var constant = Expression.Constant(value);
            var equalExp = Expression.Equal(paramPi, constant);
            var lambda = Expression.Lambda<Func<T, bool>>(equalExp, param);
            return lambda;
        }
        public static Expression<Func<T, bool>> WhereGreaterEqual<T>(string propName, object value)
        {
            var param = Expression.Parameter(typeof(T), "x");
            var paramPi = Expression.Property(param, propName);
            var constant = Expression.Constant(value);
            var equalExp = Expression.GreaterThanOrEqual(paramPi, constant);
            var lambda = Expression.Lambda<Func<T, bool>>(equalExp, param);
            return lambda;
        }
        public static Expression<Func<T, bool>> WhereLessEqual<T>(string propName, object value)
        {
            var param = Expression.Parameter(typeof(T), "x");
            var paramPi = Expression.Property(param, propName);
            var constant = Expression.Constant(value);
            var equalExp = Expression.LessThanOrEqual(paramPi, constant);
            var lambda = Expression.Lambda<Func<T, bool>>(equalExp, param);
            return lambda;
        }

        #endregion

    }
}

